package com.quantdo.qtm.mysql.service.monitor.impl;

import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.quantdo.qtm.interfaces.common.QuantdoResponse;
import com.quantdo.qtm.interfaces.mysql.pojo.monitor.query.MysqlStatusPointQuery;
import com.quantdo.qtm.mysql.dao.MonitorDao;
import com.quantdo.qtm.mysql.dao.MysqlStatusCurrDao;
import com.quantdo.qtm.mysql.dao.MysqlStatusHistoryDao;
import com.quantdo.qtm.mysql.dao.MysqlStatusPointDao;
import com.quantdo.qtm.mysql.pojo.mapper.MysqlStatusMapper;
import com.quantdo.qtm.mysql.pojo.model.MysqlMonitorStatusKeyValDo;
import com.quantdo.qtm.mysql.pojo.model.MysqlStatusCurrDo;
import com.quantdo.qtm.mysql.pojo.model.MysqlStatusHistoryDo;
import com.quantdo.qtm.mysql.pojo.model.MysqlStatusPointDo;
import com.quantdo.qtm.mysql.service.monitor.MysqlMonitorScheduledService;
import com.quantdo.qtm.mysql.service.monitor.MysqlMonitorService;

@Service
public class MysqlMonitorServiceImpl implements MysqlMonitorService, MysqlMonitorScheduledService {

    public static final String DEFAULT_VAL = "0";

    @Autowired
    private MonitorDao monitorDao;
    @Autowired
    private MysqlStatusCurrDao currDao;
    @Autowired
    private MysqlStatusPointDao pointDao;
    @Autowired
    private MysqlStatusHistoryDao historyDao;
    @Autowired
    private MysqlSingleUpdateOpareServiceImp singleUpdateOpareServiceImpl;

    @Value("${mysqlinterval}")
    private Integer intervalSecond;

    public List<MysqlStatusPointDo> selectStatus(MysqlStatusPointQuery mysqlStatusPointQuery) {
        Calendar beginTime = Calendar.getInstance();
        beginTime.add(Calendar.HOUR_OF_DAY, -24);
        mysqlStatusPointQuery.setBeginTime(beginTime.getTime());
        return pointDao.selectConditionStatus(mysqlStatusPointQuery);
    }       

    @Override
    public QuantdoResponse<?> selectQps(MysqlStatusPointQuery mysqlStatusPointQuery) {
        List<MysqlStatusPointDo> mysqlStatusPointDos = selectStatus(mysqlStatusPointQuery);
        Map<String, Object> res = new HashMap<String, Object>();
        res.put("xAxis", selectCurrTime(mysqlStatusPointDos));
        res.put("yAxis", selectItem(mysqlStatusPointDos, MysqlStatusPointDo::getQps));
        return QuantdoResponse.success(res);
    }

    @Override
    public QuantdoResponse<?> selectTps(MysqlStatusPointQuery mysqlStatusPointQuery) {
        List<MysqlStatusPointDo> mysqlStatusPointDos = selectStatus(mysqlStatusPointQuery);
        Map<String, Object> res = new HashMap<String, Object>();
        res.put("xAxis", selectCurrTime(mysqlStatusPointDos));
        res.put("yAxis", selectItem(mysqlStatusPointDos, MysqlStatusPointDo::getTps));
        return QuantdoResponse.success(res);
    }

    @Override
    public QuantdoResponse<?> selectDeleteqty(MysqlStatusPointQuery mysqlStatusPointQuery) {
        List<MysqlStatusPointDo> mysqlStatusPointDos = selectStatus(mysqlStatusPointQuery);
        Map<String, Object> res = new HashMap<String, Object>();
        res.put("xAxis", selectCurrTime(mysqlStatusPointDos));
        res.put("yAxis", selectItem(mysqlStatusPointDos, MysqlStatusPointDo::getDeleteQty));
        return QuantdoResponse.success(res);
    }

    @Override
    public QuantdoResponse<?> selectInnodbbufferpool(MysqlStatusPointQuery mysqlStatusPointQuery) {
        List<MysqlStatusPointDo> mysqlStatusPointDos = selectStatus(mysqlStatusPointQuery);
        Map<String, Object> res = new HashMap<String, Object>();
        res.put("xAxis", selectCurrTime(mysqlStatusPointDos));
        res.put("yAxis", selectItem(mysqlStatusPointDos, MysqlStatusPointDo::getInnodbBufferPoolUsedRate));
        return QuantdoResponse.success(res);
    }

    @Override
    public QuantdoResponse<?> selectInsertQty(MysqlStatusPointQuery mysqlStatusPointQuery) {
        List<MysqlStatusPointDo> mysqlStatusPointDos = selectStatus(mysqlStatusPointQuery);
        Map<String, Object> res = new HashMap<String, Object>();
        res.put("xAxis", selectCurrTime(mysqlStatusPointDos));
        res.put("yAxis", selectItem(mysqlStatusPointDos, MysqlStatusPointDo::getInsertQty));
        return QuantdoResponse.success(res);
    }

    @Override
    public QuantdoResponse<?> selectMyisamBufferPool(MysqlStatusPointQuery mysqlStatusPointQuery) {
        List<MysqlStatusPointDo> mysqlStatusPointDos = selectStatus(mysqlStatusPointQuery);
        Map<String, Object> res = new HashMap<String, Object>();
        res.put("xAxis", selectCurrTime(mysqlStatusPointDos));
        res.put("yAxis", selectItem(mysqlStatusPointDos, MysqlStatusPointDo::getKeyBlocksUsedRate));
        return QuantdoResponse.success(res);
    }

    @Override
    public QuantdoResponse<?> selectReceivedFlux(MysqlStatusPointQuery mysqlStatusPointQuery) {
        List<MysqlStatusPointDo> mysqlStatusPointDos = selectStatus(mysqlStatusPointQuery);
        Map<String, Object> res = new HashMap<String, Object>();
        res.put("xAxis", selectCurrTime(mysqlStatusPointDos));
        res.put("yAxis", selectItem(mysqlStatusPointDos, MysqlStatusPointDo::getBytesReceived));
        return QuantdoResponse.success(res);
    }

    @Override
    public QuantdoResponse<?> selectSelectQty(MysqlStatusPointQuery mysqlStatusPointQuery) {
        List<MysqlStatusPointDo> mysqlStatusPointDos = selectStatus(mysqlStatusPointQuery);
        Map<String, Object> res = new HashMap<String, Object>();
        res.put("xAxis", selectCurrTime(mysqlStatusPointDos));
        res.put("yAxis", selectItem(mysqlStatusPointDos, MysqlStatusPointDo::getSelectQty));
        return QuantdoResponse.success(res);
    }

    @Override
    public QuantdoResponse<?> selectSentFlux(MysqlStatusPointQuery mysqlStatusPointQuery) {
        List<MysqlStatusPointDo> mysqlStatusPointDos = selectStatus(mysqlStatusPointQuery);
        Map<String, Object> res = new HashMap<String, Object>();
        res.put("xAxis", selectCurrTime(mysqlStatusPointDos));
        res.put("yAxis", selectItem(mysqlStatusPointDos, MysqlStatusPointDo::getBytesSent));
        return QuantdoResponse.success(res);
    }

    @Override
    public QuantdoResponse<?> selectSlowQueries(MysqlStatusPointQuery mysqlStatusPointQuery) {
        List<MysqlStatusPointDo> mysqlStatusPointDos = selectStatus(mysqlStatusPointQuery);
        Map<String, Object> res = new HashMap<String, Object>();
        res.put("xAxis", selectCurrTime(mysqlStatusPointDos));
        res.put("yAxis", selectItem(mysqlStatusPointDos, MysqlStatusPointDo::getSlowQueries));
        return QuantdoResponse.success(res);
    }

    @Override
    public QuantdoResponse<?> selectThreadConnedQty(MysqlStatusPointQuery mysqlStatusPointQuery) {
        List<MysqlStatusPointDo> mysqlStatusPointDos = selectStatus(mysqlStatusPointQuery);
        Map<String, Object> res = new HashMap<String, Object>();
        res.put("xAxis", selectCurrTime(mysqlStatusPointDos));
        res.put("yAxis", selectItem(mysqlStatusPointDos, MysqlStatusPointDo::getThreadsConnected));
        return QuantdoResponse.success(res);
    }

    @Override
    public QuantdoResponse<?> selectThreadConningQty(MysqlStatusPointQuery mysqlStatusPointQuery) {
        List<MysqlStatusPointDo> mysqlStatusPointDos = selectStatus(mysqlStatusPointQuery);
        Map<String, Object> res = new HashMap<String, Object>();
        res.put("xAxis", selectCurrTime(mysqlStatusPointDos));
        res.put("yAxis", selectItem(mysqlStatusPointDos, MysqlStatusPointDo::getThreadsRunning));
        return QuantdoResponse.success(res);
    }

    @Override
    public QuantdoResponse<?> selectUpdateQty(MysqlStatusPointQuery mysqlStatusPointQuery) {
        List<MysqlStatusPointDo> mysqlStatusPointDos = selectStatus(mysqlStatusPointQuery);
        Map<String, Object> res = new HashMap<String, Object>();
        res.put("xAxis", selectCurrTime(mysqlStatusPointDos));
        res.put("yAxis", selectItem(mysqlStatusPointDos, MysqlStatusPointDo::getUpdateQty));
        return QuantdoResponse.success(res);
    }

    @Override
    public QuantdoResponse<?> selectWriteQty(MysqlStatusPointQuery mysqlStatusPointQuery) {
        List<MysqlStatusPointDo> mysqlStatusPointDos = selectStatus(mysqlStatusPointQuery);
        Map<String, Object> res = new HashMap<String, Object>();
        res.put("xAxis", selectCurrTime(mysqlStatusPointDos));
        res.put("yAxis", selectItem(mysqlStatusPointDos, MysqlStatusPointDo::getWriteQty));
        return QuantdoResponse.success(res);
    }

    @Override
    @Transactional
    public int updateStatus() {
        List<MysqlMonitorStatusKeyValDo> res = monitorDao.selectStatusKeyVal();
        Map<String, Object> statusAttr = res.stream().collect(Collectors.toMap(MysqlMonitorStatusKeyValDo::getVariableName, MysqlMonitorStatusKeyValDo::getValue));
        statusAttr.put(MysqlStatusMapper.CURR_TIME, new Date());
        statusAttr.put(MysqlStatusMapper.INTERVAL_SECOND, intervalSecond);
        System.out.println(statusAttr);

        // 当前的Mysql数据库状态
        MysqlStatusPointDo pointDo = new MysqlStatusPointDo();
        MysqlStatusHistoryDo historyDo = new MysqlStatusHistoryDo();
        MysqlStatusCurrDo statusCurrNewDo = new MysqlStatusCurrDo();
        statusCurrNewDo.setCurrTime((Date) statusAttr.get(MysqlStatusMapper.CURR_TIME));
        statusCurrNewDo.setIntervalSecond((Integer) statusAttr.get(MysqlStatusMapper.INTERVAL_SECOND));
        statusCurrNewDo.setQuestions(Integer.parseInt(Objects.toString(statusAttr.get(MysqlStatusMapper.QUESTIONS), DEFAULT_VAL)));
        statusCurrNewDo.setComCommit(Integer.parseInt(Objects.toString(statusAttr.get(MysqlStatusMapper.COM_COMMIT), DEFAULT_VAL)));
        statusCurrNewDo.setComRollback(Integer.parseInt(Objects.toString(statusAttr.get(MysqlStatusMapper.COM_ROLLBACK), DEFAULT_VAL)));
        statusCurrNewDo.setComSelect(Integer.parseInt(Objects.toString(statusAttr.get(MysqlStatusMapper.COM_SELECT), DEFAULT_VAL)));
        statusCurrNewDo.setComInsert(Integer.parseInt(Objects.toString(statusAttr.get(MysqlStatusMapper.COM_INSERT), DEFAULT_VAL)));
        statusCurrNewDo.setComUpdate(Integer.parseInt(Objects.toString(statusAttr.get(MysqlStatusMapper.COM_UPDATE), DEFAULT_VAL)));
        statusCurrNewDo.setComDelete(Integer.parseInt(Objects.toString(statusAttr.get(MysqlStatusMapper.COM_DELETE), DEFAULT_VAL)));
        statusCurrNewDo.setThreadsConnected(Integer.parseInt(Objects.toString(statusAttr.get(MysqlStatusMapper.THREADS_CONNECTED), DEFAULT_VAL)));
        statusCurrNewDo.setThreadsRunning(Integer.parseInt(Objects.toString(statusAttr.get(MysqlStatusMapper.THREADS_RUNNING), DEFAULT_VAL)));
        statusCurrNewDo.setSlowQueries(Integer.parseInt(Objects.toString(statusAttr.get(MysqlStatusMapper.SLOW_QUERIES), DEFAULT_VAL)));
        statusCurrNewDo.setBytesSent(new BigDecimal(Objects.toString(statusAttr.get(MysqlStatusMapper.BYTES_SENT), DEFAULT_VAL)).divide(new BigDecimal(100)).intValue());
        statusCurrNewDo.setBytesReceived(new BigDecimal(Objects.toString(statusAttr.get(MysqlStatusMapper.BYTES_RECEIVED), DEFAULT_VAL)).divide(new BigDecimal(100)).intValue());
        statusCurrNewDo.setInnodbBufferPoolPagesTotal(Integer.parseInt(Objects.toString(statusAttr.get(MysqlStatusMapper.INNODB_BUFFER_POOL_PAGES_TOTAL), DEFAULT_VAL)));
        statusCurrNewDo.setInnodbBufferPoolPagesFree(Integer.parseInt(Objects.toString(statusAttr.get(MysqlStatusMapper.INNODB_BUFFER_POOL_PAGES_FREE), DEFAULT_VAL)));
        statusCurrNewDo.setKeyBlocksUsed(Integer.parseInt(Objects.toString(statusAttr.get(MysqlStatusMapper.KEY_BLOCKS_USED), DEFAULT_VAL)));
        statusCurrNewDo.setKeyBlocksUnused(Integer.parseInt(Objects.toString(statusAttr.get(MysqlStatusMapper.KEY_BLOCKS_UNUSED), DEFAULT_VAL)));

        MysqlStatusCurrDo statusCurrOldDo = currDao.select();
        singleUpdateOpare(statusCurrNewDo, statusCurrOldDo, pointDo);
        BeanUtils.copyProperties(statusCurrNewDo, historyDo);
        historyDao.insert(historyDo);
        currDao.delete(statusCurrOldDo);
        currDao.insert(statusCurrNewDo);
        pointDo.setCurrTime(statusCurrNewDo.getCurrTime());
        pointDo.setIntervalSecond(statusCurrNewDo.getIntervalSecond());
        pointDao.insert(pointDo);
        return 1;
    }

    private List<?> selectItem(List<MysqlStatusPointDo> mysqlStatusPointDos, Function<? super MysqlStatusPointDo, ? extends Integer> mapper){
        return mysqlStatusPointDos.stream().map(mapper).collect(Collectors.toList());
    }
    private List<?> selectCurrTime(List<MysqlStatusPointDo> mysqlStatusPointDos){
        return mysqlStatusPointDos.stream().map(MysqlStatusPointDo::getFormatTime).collect(Collectors.toList());
    }

    private void singleUpdateOpare(MysqlStatusCurrDo statusCurrNewDo, MysqlStatusCurrDo statusCurrOldDo, MysqlStatusPointDo pointDo) {
        singleUpdateOpareServiceImpl.updateOpare(statusCurrNewDo, statusCurrOldDo, pointDo);
    }
    
}
